package net.gdface.cassdk;

import java.awt.image.BufferedImage;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import net.gdface.cassdk.jna.FaceAngle;
import net.gdface.cassdk.jna.THFI_FacePos;
import net.gdface.cassdk.jna.tagPOINT;
import net.gdface.cassdk.jna.tagRECT;
import net.gdface.image.ImageErrorException;
import net.gdface.image.LazyImage;
import net.gdface.sdk.BaseFaceApiLocal;
import net.gdface.sdk.CodeInfo;
import net.gdface.sdk.EyeInfo;
import net.gdface.sdk.FAngle;
import net.gdface.sdk.FInt2;
import net.gdface.sdk.FRect;
import net.gdface.sdk.NotFaceDetectedException;
import net.gdface.sdk.fse.FeatureSe;
import net.gdface.utils.FaceUtilitsX;
import net.gdface.utils.SampleLog;

/**
 * CASSDK接口实现类
 * 
 * @author guyadong
 *
 */
public class FaceApiCas extends BaseFaceApiLocal {
	private static final FaceApiCas INSTANCE = new FaceApiCas();
	static {
		if (!FaceUtilitsX.isBuilding()) {
			try {
				// CASSDK JNI接口静态区初始化
				Class.forName(net.gdface.cassdk.JniBridge.class.getName());
			} catch (ClassNotFoundException e) {
				SampleLog.log(e.getMessage());
				throw new ExceptionInInitializerError(e);
			}
		}
	}
	
	protected FaceApiCas() {		
	}
	/**
	 * @return the instance
	 */
	public static FaceApiCas getInstance() {
		return INSTANCE;
	}

	@Override
	protected double nativeCompareCode(byte[] code1, byte[] code2) {
		return (double)JniBridge.compareFeature(code1, code2);
	}

	/**
	 * 底层类型->通用类型
	 * @param rect
	 * @return FRect,rect为{@code null}时返回{@code null}
	 */	
	static private FRect makeFRect(tagRECT rect){
		return null==rect
				?null
				:new FRect(
						rect.left
						,rect.top
						,rect.right-rect.left+1
						,rect.bottom-rect.top+1);		
	}
	/**
	 * 通用类型->底层类型
	 * @param rect
	 * @return FaceRect,rect为{@code null}时返回{@code null}
	 */
	private static final tagRECT toNative(FRect rect){
		return null==rect
				?null
				:new tagRECT(
						rect.getLeft()
						,rect.getTop()
						,rect.getLeft()+rect.getWidth()-1
						,rect.getTop()+rect.getHeight()-1);		
	}
	/**
	 * 底层类型->通用类型
	 * @param leftEye 左眼位置
	 * @param rightEye 右眼位置
	 * @return EyeInfo,leftEye或rightEye为{@code null}或所有的值为0时返回{@code null}
	 */
	private static final EyeInfo makeEyeInfo(tagPOINT	leftEye,tagPOINT	rightEye){		
		return null==leftEye||null==rightEye||(0==leftEye.x&&0==leftEye.y&&0==rightEye.x&&0==rightEye.y)
				?null
				:new EyeInfo(leftEye.x,leftEye.y,rightEye.x,rightEye.y);		
	}
	/**
	 * 通用类型->底层类型
	 * @param ei
	 * @return FacePoint,ei为{@code null}或所有的值为0时返回{@code null}
	 */
	private static final tagPOINT toNativeLeft(EyeInfo ei){
		return null==ei||(0==ei.getLeftx()&&0==ei.getLefty())
				?null:new tagPOINT(ei.getLeftx(),ei.getLefty());
	}
	/**
	 * 通用类型->底层类型
	 * @param ei
	 * @return FacePoint,ei为{@code null}或所有的值为0时返回{@code null}
	 */
	private static final tagPOINT toNativeRight(EyeInfo ei){
		return null==ei||(0==ei.getRightx()&&0==ei.getRighty())
				?null:new tagPOINT(ei.getRightx(),ei.getRighty());
	}
	/**
	 * 底层类型->通用类型
	 * @param point
	 * @return FInt2,point为{@code null}或所有的值为0时返回{@code null}
	 */
	private static final FInt2 makeFInt2(tagPOINT point){
		return null==point||(0==point.x&&0==point.y)
				?null:new FInt2(point.x,point.y);
	}
	/**
	 * 通用类型->底层类型
	 * @param point
	 * @return FacePoint,point为{@code null}或所有的值为0时返回{@code null}
	 */
	private static final tagPOINT toNative(FInt2 point){
		return null==point||(0==point.getX()&&0==point.getY())
				?null:new tagPOINT(point.getX(),point.getY());
	}
	/**
	 * 底层类型->通用类型
	 * @param angle
	 * @return FAngle,angle为{@code null}或所有的值为0时返回{@code null}
	 */
	private static final FAngle makeFAngle(FaceAngle angle){
		return null==angle||(0==angle.yaw&&0==angle.pitch&&0==angle.roll)
				?null:new FAngle(angle.yaw,angle.pitch,angle.roll,angle.confidence);
	}
	/**
	 * 通用类型->底层类型
	 * @param angle
	 * @return FAngle,angle为{@code null}或所有的值为0时返回{@code null}
	 */
	private static final FaceAngle toNative(FAngle angle){
		return null==angle||(0==angle.getYaw()&&0==angle.getPitch()&&0==angle.getRoll())
				?null:new FaceAngle(angle.getYaw(),angle.getPitch(),angle.getRoll(),angle.getConfidence());
	}
	/**
	 * 底层类型->通用类型
	 * @param ci
	 * @return CodeInfo,ci为{@code null}时返回{@code null}
	 */
	private static final CodeInfo makeCodeInfo(THFI_FacePos ci){
		return null==ci?null:
			new CodeInfo(null
					, makeFRect(ci.rcFace)
					, makeEyeInfo(ci.ptLeftEye,ci.ptRightEye)
					, makeFInt2(ci.ptMouth)
					, makeFInt2(ci.ptNose)
					, makeFAngle(ci.fAngle)
					, ci.pFacialData);
	}
	/**
	 * 通用类型->底层类型
	 * @param pos
	 * @return FacePos,pos为{@code null}时返回{@code null}
	 */
	private static final THFI_FacePos toNative(CodeInfo pos){
		return null==pos?null:
			new THFI_FacePos(
					  toNative(pos.getPos())
					, toNativeLeft(pos.getEi())
					, toNativeRight(pos.getEi())
					, toNative(pos.getMouth())
					, toNative(pos.getNose())
					, toNative(pos.getAngle())
					, 0
					, pos.getFacialData());
	}
	@Override
	public final String sdkVersion() {
		return BaseJniBridge.SDK_VERSION;
	}
	@Override
	public void nativeDetectFace(byte[] imgRGB, int width, int height, List<CodeInfo> faceInfo) {
		THFI_FacePos[] posArray = JniBridge.detectFace(imgRGB, 24, width, height);
		for (THFI_FacePos pos : posArray){
			faceInfo.add(makeCodeInfo(pos));
		}
	}
	@Override
	public byte[] nativeGetFaceFeature(byte[] imgMatrix, int width, int height, CodeInfo faceInfo) {
		return JniBridge.getFaceFeature(imgMatrix, width, height, toNative(faceInfo));
	}
	@Override
	public byte[][] nativeGetFaceFeatures(byte[] imgMatrix, int width, int height, List<CodeInfo> facePos) {
		THFI_FacePos[] nativeArray = new THFI_FacePos[facePos.size()];
		for(int i=0;i<nativeArray.length;++i){
			nativeArray[i]=toNative(facePos.get(i));
		}
		return JniBridge.getFaceFeatures(imgMatrix, width, height, nativeArray);			
	}
	@Override
	public CodeInfo[] nativeDetectAndGetFeatures(byte[] imgMatrix, int width, int height) {
		LinkedHashMap<THFI_FacePos, byte[]> facePos = JniBridge.detectAndGetFeatures(imgMatrix, width,height);
		CodeInfo[] ci = new CodeInfo[facePos.size()];
		int i = 0;
		CodeInfo element = null; 
		for(Entry<THFI_FacePos, byte[]> entry:facePos.entrySet()){
			element = makeCodeInfo(entry.getKey());
			element.setCode(entry.getValue());
			ci[ i++ ] = element;
		}
		return ci;
	}
	@Override
	public FeatureSe getFeatureSe(){
		try{
			return FseBridge.getFeatureSe();
		}catch(UnsatisfiedLinkError e){
			SampleLog.log("WARN:fail to load native feature_se dynamic library caused by UnsatisfiedLinkError:{}",e.getMessage());
			return null;
		}
	}
	/**
	 * 检测人脸
	 * @param bufferedImage
	 * @return
	 * @see BaseCassdk#detectFace(net.gdface.image.BaseLazyImage)
	 */
	public List<CodeInfo> detectFace(BufferedImage bufferedImage){
		try {
			return detectFace(LazyImage.create(bufferedImage));
		} catch (ImageErrorException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 提取特征
	 * @param bufferedImage
	 * @param faceNum
	 * @param facePos
	 * @return
	 * @throws NotFaceDetectedException
	 * @see {@link BaseCassdk#detectAndGetCodeInfo(net.gdface.image.BaseLazyImage, int)}
	 */
	public List<CodeInfo> getCodeInfo(BufferedImage bufferedImage, int faceNum, List<CodeInfo> facePos) 
			throws NotFaceDetectedException{
		return getCodeInfo(LazyImage.create(bufferedImage), faceNum, facePos);
	}
	/**
	 * 检测并提取人脸特征
	 * @param bufferedImage
	 * @param faceNum
	 * @return
	 * @throws NotFaceDetectedException
	 * @see {@link BaseCassdk#detectAndGetCodeInfo(net.gdface.image.BaseLazyImage, int)}
	 */
	public CodeInfo[] detectAndGetCodeInfo(BufferedImage bufferedImage, int faceNum) 
			throws NotFaceDetectedException {
		try {
			return detectAndGetCodeInfo(LazyImage.create(bufferedImage).open(), faceNum);
		} catch (ImageErrorException e) {
			throw new RuntimeException(e);
		}
	}
}
